#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author : hai.long
# Comment : requests二次封装类
import re
import urllib3
import requests
import threading
import json as json_
import jmespath


# 断言类封装
class AssertTool(object):
    msg = ""

    def info(self, message):
        self.msg += f"{message}\n"

    def assert_equal(self, check_value, expect_value, prefix):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :param prefix: prefix
        :return: null
        """
        res = check_value == expect_value
        if res:

            self.info(f"assert {check_value} equal {expect_value}	==> pass")
        else:
            self.info(f"assert {check_value} equal {expect_value}	==> fail")
            self.info(f"check_item: {check_value}")
            self.info(f"check_value: {check_value}")
            self.info("assert_method: equal")
            self.info(f"expect_value: {expect_value}")

    def assert_contains(self, check_value, expect_value):
        """
        实际结果 包含 预期结果
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        res = check_value in expect_value
        if res:
            self.info(f"assert {check_value} contains {expect_value}	==> pass")
        else:
            self.info(f"assert {check_value} contains {expect_value}	==> fail")
            self.info(f"check_item: {check_value}")
            self.info(f"check_value: {check_value}")
            self.info("assert_method: contains")
            self.info(f"expect_value: {expect_value}")
        return {"code": 200, "msg": self.msg}

    def assert_contained_by(self, check_value, expect_value):
        """
        预期结果 包含 实际结果
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        res = expect_value in check_value
        if res:
            self.info(f"assert {expect_value} contains {check_value}({type(check_value)})	==> pass")
        else:
            self.info(f"assert {expect_value} contains {check_value}({type(check_value)})	==> fail")
        self.info(f"check_item: {check_value}")
        self.info(f"check_value: {check_value}({type(check_value)})")
        self.info("assert_method: contains")
        self.info(f"expect_value: {expect_value}({type(expect_value)})")

    def assert_startswith(self, check_value: str, expect_value: str):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        try:
            assert check_value.startswith(expect_value)
        except Exception as e:
            print(e)

    def assert_regex_match(self, check_value, expect_value):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        res = re.match(expect_value, check_value)
        if res is not None:
            self.info(f"assert {res.group()} regex_match {check_value}	==> pass")
        else:
            self.info(f"assert {expect_value} regex_match {check_value}	==> fail")
            self.info(f"check_item: {expect_value}")
            self.info(f"check_value: {check_value}")
            self.info("assert_method: regex_match")
            self.info(f"expect_value: {check_value}")

    def assert_endswith(self, check_value: str, expect_value: str):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        res = check_value.endswith(expect_value)
        if res:
            self.info(f"assert {check_value} endswith {expect_value}	==> pass")
        else:
            self.info(f"assert {check_value} endswith {expect_value}	==> fail")
            self.info(f"check_item: {expect_value}")
            self.info(f"check_value: {check_value}")
            self.info("assert_method: endswith")
            self.info(f"expect_value: {expect_value}")

    def assert_length_equal(self, check_value, expect_value: int):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        res = len(check_value) == expect_value
        if res:
            self.info(f"assert {check_value} length_equal {expect_value}	==> pass")
            self.msg += f"assert {check_value} length_equal {expect_value}	==> pass<br>"
        else:
            self.info(f"assert {check_value} length_equal {expect_value}	==> fail")
            self.info(f"check_item: {check_value}")
            self.info(f"check_value: {check_value}")
            self.info("assert_method: length_equal")
            self.info(f"expect_value: {expect_value}")
        return {"code": 200, "msg": self.msg}

    def assert_length_greater_than(self, check_value, expect_value):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        try:
            assert len(check_value) > len(expect_value)
        except Exception as e:
            print(e)

    def assert_length_greater_or_equals(self, check_value, expect_value):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        try:
            assert len(check_value) >= len(expect_value)
        except Exception as e:
            print(e)

    def assert_greater_or_equals(self, check_value, expect_value):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        try:
            assert check_value >= expect_value
        except Exception as e:
            print(e)

    def assert_greater_than(self, check_value, expect_value):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        try:
            assert check_value > expect_value
        except Exception as e:
            print(e)

    def assert_length_less_or_equals(self, check_value, expect_value):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        try:
            assert len(check_value) <= len(expect_value)
        except Exception as e:
            print(e)

    def assert_length_less_than(self, check_value, expect_value):
        """
        :param check_value: 实际结果
        :param expect_value: 预期结果
        :return: null
        """
        try:
            assert len(check_value) < len(expect_value)
        except Exception as e:
            print(e)


class BaseRequest(object):
    assert_tool = AssertTool()
    info_log = ""
    error_log = ""
    lock = threading.Lock()  # 初始化锁对象

    def info(self, message):
        self.info_log += f"{message}\n"

    def error(self, message):
        self.error_log += f"{message}\n"

    def request(self, method, url: str, json=None, params=None, token=None, headers=None, files=None, verify=False,
                setup=None, teardown=None, extract_list=None, validate_list=None, prefix=None,
                t_type=1):
        """
        发送request请求
        :param method: 请求方式
        :param url: 请求的url
        :param json: 请求体，含body
        :param params: 请求参数，表格参数
        :param token: 鉴权token，jwt
        :param headers:
        :param files: 请求文件
        :param verify: 证书验证 默认不验证
        :param setup: setup
        :param teardown: teardown
        :param extract_list: extract
        :param validate_list: validate
        :param prefix: prefix
        :param t_type: t_type
        :return:
        """
        try:
            self.lock.acquire()  # 加锁
            self.assert_tool.msg = ""
            request_info = "<br>====================== request  details ======================<br>"
            request_info += 'method : {}<br>'.format(method)
            request_info += 'url         : {}<br>'.format(url)
            _url = url
            if token:
                header = ""
            else:
                header = headers
            request_info += 'headers : {} <br>'.format(header)
            payload = None
            if json:
                payload = {}
                for k, v in json.items():
                    if v != None:
                        payload[k] = v
                request_info += 'body     : {}<br>'.format(json_.dumps(payload, indent=4, ensure_ascii=False))
            # if params:
            #     exclude_params = {}
            #     for k, v in params.items():
            #         if v is not None:
            #             exclude_params[k] = v
            #     params = exclude_params
            #     self.result_log += 'params : {}<br>'.format(params)
            #     request_info += 'params : {}<br>'.format(params)
            self.info(request_info[:-4])
            try:
                urllib3.disable_warnings()
                response = requests.request(
                    method=method,
                    url=_url,
                    json=payload,
                    params=params,
                    headers=header,
                    files=files,
                    verify=verify,
                    allow_redirects=True
                )
            except requests.RequestException as e:
                self.error('RequestException URL : %s' % url)
                self.error('RequestException Info: %s' % e)
                self.lock.release()  # 释放锁
                return None

            except Exception as e:
                self.error('Exception URL : %s' % url)
                self.error('Exception Info: %s' % e)
                self.lock.release()  # 释放锁
                return None

            time_total = response.elapsed.total_seconds()
            status_code = response.status_code
            response_info = '<br>====================== response details ======================<br>'
            response_info += 'url         : {}<br>'.format(response.url)
            response_info += 'method : {}<br>'.format(method.upper())
            response_info += 'headers : {}<br>'.format(header)

            if json:
                response_info += f'body     : {json_.dumps(payload, indent=4, ensure_ascii=False)}<br>'
            if params:
                response_info += f'params : {params}<br>'
            if files:
                response_info += f'files : {files}<br>'
            response_info += 'status_code : {}<br>'.format(status_code)
            response_info += 'time_total : {} s<br>'.format(time_total)
            if "application/json" in response.headers.get("Content-Type"):
                response_info += 'body     : %s<br>' % json_.dumps(response.json(), indent=4, ensure_ascii=False)
            else:
                response_info += 'text : %s<br>' % response.content
            self.info(response_info[:-4])
            # 判断是否需要提取变量
            extract_result = {}
            if status_code == 200:
                for jmespath_item in extract_list:
                    j_path, var_name = jmespath_item["jmespath"], jmespath_item["var_name"]
                    prefix_jmespath = j_path.split('.')[0]
                    if prefix_jmespath == "status_code":
                        extract_result[var_name] = response.status_code
                    elif prefix_jmespath == "body":
                        extract_result[var_name] = jmespath.search(j_path, {"body": response.json()})
                    elif prefix_jmespath == "headers":
                        extract_result[var_name] = jmespath.search(j_path, {"headers": response.headers})
                    elif prefix_jmespath == "encoding":
                        extract_result[var_name] = response.encoding
                    elif prefix_jmespath == "cookies":
                        extract_result[var_name] = response.cookies.values()

                # 判断是否需要断言
                if validate_list is not None:
                    response_body = {"body": response.json()}
                    if isinstance(validate_list, list):
                        for validate in validate_list:
                            check_value = validate["expression"]
                            expect_value = validate["value"]
                            try:
                                expect_value = int(expect_value)
                            except Exception as e:
                                print(e)
                            if check_value == "status_code":
                                check_value = response.status_code
                            else:
                                check_value = jmespath.search(check_value, response_body)
                            getattr(self.assert_tool, validate["relationship"])(check_value, expect_value, prefix)
            self.lock.release()  # 释放锁
            return {"status_code": status_code, "extract_result": extract_result}
        except Exception as e:
            print(e)
            return None

    def extract_variables(self, data, jmespath: str, var_name: str, prefix, t_type):
        """
        变量提取存储到缓存数据库
        :param data: 数据来源
        :param jmespath: 变量名称
        :param var_name: 变量值
        :param prefix: prefix
        :param t_type: t_type
        :return: null
        """
        value = jmespath.search(jmespath, data)
        print(value)
        key_name = f"{prefix}﹏{var_name}"
        if value is None:
            value = ""
        # 如果提取结果为json，则转为字符串
        if value == "":
            self.info("extract mapping: {'%s':%s}" % (var_name, None))
        else:
            if isinstance(value, str):
                self.info("extract mapping: {'%s':'%s'}" % (var_name, value))
            else:
                self.info("extract mapping: {'%s':%s}" % (var_name, value))


class AssertRequest(object):
    # 相等
    def equal(self, check, expect):
        return check == expect

    # 小于less_than
    def less_than(self, check, expect):
        return check < expect

    # 小于或等于
    def less_or_equals(self, check, expect):
        return check <= expect

    # 大于greater_than
    def greater_than(self, check, expect):
        return check > expect

    # 大于或等于greater_or_equals
    def greater_or_equals(self, check, expect):
        return check >= expect

    # 不等于not_equal
    def not_equal(self, check, expect):
        return check != expect

    # 转字符串相等string_equals
    def string_equals(self, check, expect):
        return str(check) == str(expect)

    # 长度相等length_equal
    def length_equal(self, check, expect):
        return len(check) < len(expect)

    # 长度大于length_greater_than
    def length_greater_than(self, check, expect):
        return len(check) > len(expect)

    # 长度大于或等于length_greater_or_equals
    def length_greater_or_equals(self, check, expect):
        return len(check) >= len(expect)

    # 长度小于length_less_than
    def length_less_than(self, check, expect):
        return len(check) < len(expect)

    # 长度小于或等于length_less_or_equals
    def length_less_or_equals(self, check, expect):
        return len(check) <= len(expect)

    # 实际包含预期contains
    def contains(self, check, expect):
        return expect in check

    # 预期包含实际contained_by
    def contained_by(self, check, expect):
        return check in expect

    # type类型匹配type_match
    def type_match(self, check, expect):
        return type(check) < type(expect)

    # 正则匹配regex_match
    def regex_match(self, check, expect):
        return True if re.findall(".*?\$\{(.*?)\}.*?", check) == expect else False

    # 字符串以xx开头startswith
    def startswith(self, check, expect):
        return str(check).startswith(expect)

    # 字符串以xx结尾endswith
    def endswith(self, check, expect):
        return str(check).endswith(expect)


def my_api_test():
    request = BaseRequest()
    request.result_log = ""
    url = "http://admintest.snkoudai.com/api/adminuser/admin/login/login"
    payload = {'username': 'longhai', 'password': 'longhai123&'}
    res = request.request("POST", url=url, json=payload, verify=False)
    url = "http://admintest.snkoudai.com/api/user/admin/company/editSupervisionFlag"
    headers = {"accessToken": res.json()["obj"]["accessToken"]}
    payload = {"companyId": 13304, "supervisionFlag": 1}
    res = request.request("POST", url=url, json=payload, headers=headers, verify=False)
    print(res.status_code)
    print(res.content)
    print(res.encoding)
    print(res.text)
    print(res.headers)


AssertRequest = AssertRequest()
if __name__ == '__main__':
    # my_api_test()
    d = {"a": 1, "b": 2}
    for k,v in enumerate(d):
        print(k,v)